package cn.yx.common.core.util.http.okhttp;

import cn.hutool.core.collection.CollectionUtil;
import cn.yx.common.core.enums.CharsetEnum;
import cn.yx.common.core.enums.MimeTypeEnum;
import cn.yx.common.core.enums.http.HttpHeaderEnum;
import cn.yx.common.core.enums.security.CertTypeEnum;
import cn.yx.common.core.exception.CommonException;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;

import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * <p>HttpBasic，为HttpGetUtil，HttpPostUtil等类的父类</p>
 *
 * @author Wgssmart
 */
@Slf4j
public class HttpBasic {

    /**
     * 用于接收服务端返回的cookies数据
     */
    private static List<Cookie> cookies;

    /**
     * 获取服务端返回的cookie数据
     *
     * @return 服务端返回的cookie数据
     */
    public static List<Cookie> getCookies() {
        return cookies;
    }

    /**
     * 刷新headers，可能需要填充Content-Type和Accept
     *
     * @param headers
     * @param dataTypeEnum
     */
    public static void refreshHeaders(Map<String, String> headers, MimeTypeEnum dataTypeEnum) {
        if (CollectionUtil.isNotEmpty(headers)) {
            if (!headers.containsKey(HttpHeaderEnum.CONTENT_TYPE.getName())) {
                headers.put(HttpHeaderEnum.CONTENT_TYPE.getName(), dataTypeEnum.getMime() + "; charset=" + CharsetEnum.UTF8.getValue());
            }
            if (!headers.containsKey(HttpHeaderEnum.ACCEPT.getName())) {
                headers.put(HttpHeaderEnum.ACCEPT.getName(), dataTypeEnum.getMime());
            }
        }
    }

    /**
     * 设置请求头
     *
     * @param headers
     */
    public static void setHeaders(Request.Builder requestBuilder, Map<String, String> headers) {
        if (CollectionUtil.isNotEmpty(headers)) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                requestBuilder.addHeader(entry.getKey(), entry.getValue());
            }
        }
    }

    /**
     * 发送HTTP请求
     *
     * @param request Request请求对象
     * @return 响应数据字符串
     */
    public static String executeRequest(Request request, List<Cookie> cookieList, boolean returnCookie) {
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .cookieJar(cookieJar(cookieList, returnCookie))
                .build();
        return execute(okHttpClient, request);
    }

    /**
     * 发送HTTP请求
     *
     * @param request Request请求对象
     * @return 响应数据字符串
     */
    public static String executeRequest(Request request, List<Cookie> cookieList, boolean returnCookie,
                                        String certPath, String certPassword, String certType, String sslProtocol) {
        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
        setSslSocketFactory(clientBuilder, certPath, certPassword, certType, sslProtocol);
        OkHttpClient okHttpClient = clientBuilder.cookieJar(cookieJar(cookieList, returnCookie))
                .build();
        return execute(okHttpClient, request);
    }

    private static String execute(OkHttpClient okHttpClient, Request request) {
        try (Response response = okHttpClient.newCall(request).execute()) {
            ResponseBody responseBody = response.body();
            if (Objects.nonNull(responseBody)) {
                return responseBody.string();
            }
            throw new CommonException("cn.yx.common.core.executeHttpError");
        } catch (IOException e) {
            log.error("ok http client execute error: {}", e.getMessage());
            // @SimplifiedChinese=HTTP请求失败
            // @English=Http execute error
            throw new CommonException("cn.yx.common.core.executeHttpError", e);
        }
    }

    private static CookieJar cookieJar(List<Cookie> cookieList, boolean returnCookie) {
        return new CookieJar() {
            @Override
            public void saveFromResponse(@NotNull HttpUrl httpUrl, @NotNull List<Cookie> cookies) {
                HttpBasic.cookies = null;
                if (returnCookie) {
                    HttpBasic.cookies = cookies;
                }
            }

            @NotNull
            @Override
            public List<Cookie> loadForRequest(@NotNull HttpUrl httpUrl) {
                return cookieList;
            }
        };
    }

    private static void setSslSocketFactory(OkHttpClient.Builder clientBuilder, String certPath, String certPassword,
                                            String certType, String sslProtocol) {
        try {
            // 设置证书类型为X.509
            CertificateFactory factory = CertificateFactory.getInstance(CertTypeEnum.X509.getValue());
            KeyStore keyStore = KeyStore.getInstance(certType);
            keyStore.load(null, null);
            InputStream inputStream = new FileInputStream(certPath);
            Certificate certificate = factory.generateCertificate(inputStream);
            keyStore.setCertificateEntry("alias", certificate);

            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, certPassword.toCharArray());
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);

            SSLContext sslContext = SSLContext.getInstance(sslProtocol);
            sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            if (trustManagers.length == 1 && trustManagers[0] instanceof X509TrustManager) {
                X509TrustManager x509TrustManager = (X509TrustManager) trustManagers[0];
                clientBuilder.sslSocketFactory(sslSocketFactory, x509TrustManager);
            }
        } catch (Exception e) {
            log.error("setSslSocketFactory error: {}", e.getMessage(), e);
            // @SimplifiedChinese=配置SSL请求失败
            // @English=Config SSL request error
            throw new CommonException("cn.yx.common.core.setSslSocketFactoryError", e);
        }
    }

}
